[S05E06] RxJS 運算子全面解析
https://www.youtube.com/watch?v=DPyZq74V60o&list=PL9LUW6O9WZqgUMHwDsKQf3prtqVvjGZ6S&index=17
今天由Will保哥分享,很多時間是大家在想operator的實際應用
這一集片長2個小時,所以拆成2天po
延申閱讀:
組合多個Observable形成新的Observable
常用的例如:
https://reactive.how/combinelatest
有2個Observable,總是會組成1個Observable
merge
有其中1個Observable有值,就會merge值
(範例中沒有展示,同時間有不同的值的case)
combineLatest
https://rxjs-dev.firebaseapp.com/api/index/function/combineLatest
兩個observable都有值才會combine,而且只combine最新的值
使用情境:
1、當實現複合時的查詢OR排序變更時,只要其中1個變更,就重新觸發server load
2、兩個form group要聯合做資料驗證,合併2個value change的observable做觸發驗證
從 RxJS 6 以後,import變成2個部分
import { defer, fromEvent, interval } from 'rxjs'; // 其他的
import { filter } from 'rxjs/operators'; // 跟序列處理相關的
// combineLatest(...observables: any[]): Observable<{}[]>
^^^^^^^^^^^^^^ 要放幾個observables的陣列都可以
var ob = combineLastest(
fromEvent(document,'click'), // 第1個Observable
fromEvent(document,'dblclick'),
(v1,v2) => ([v1,v2]) // 傳陣列 // resultSelector:(v1:{},v2:{})=>{}[]
// (v1,v2) => ({v1,v2}) // 傳物件
^^ 第1個Observable丟進來的event
).pipe(做一些事).subscribe(([v1,v2]) => {
^^^^^^^^ 或者寫v,就是([v1,v2])
console.log(`v1.clientX = ${v1.clientX], v2.clientX = ${v2.clientX}`);
});
https://rxjs-dev.firebaseapp.com/api/index/function/combineLatest
也是合併Observables的,看官網文件的圖比較好理解Observables要complete
才會吐出combine的東西
Combines multiple Observables to create an Observable whose values are calculated from the latest values of each of its input Observables.
combineLatest<O extends ObservableInput<any>, R>(...observables: (O | ((...values: ObservedValueOf<O>[]) => R) | SchedulerLike)[]): Observable<R>
var ob = forkJoin(
fromEvent(document,'click').pipe(take(1)),
^^^^^^^只取1個,否則event stream不會停止
fromEvent(document,'dblclick').pipe(take(1)),
).pipe(做一些事).subscribe(([v1,v2]) => {
^^^^^^^^ 或者寫v,就是([v1,v2])
console.log(`v1.clientX = ${v1.clientX], v2.clientX = ${v2.clientX}`);
});
var ob=fromEvent(document,'click')
.pipe(startWith(1,2,3)) // 多形最多6個,超過要放在陣列
^^^^^^^^^^^^^^^ 不用按click,就會先丟3個序列
.subscribe(v=>{
console.log(v);
});
https://rxjs-dev.firebaseapp.com/api/index/function/iif
看官網的範例比較好理解
import { iif, of } from 'rxjs';
let subscribeToFirst;
const firstOrSecond = iif(
() => subscribeToFirst, // 給一個callback function
of('first'), // 如果為ture,就回傳第1個檔observable
of('second'), // 如果為false,就回傳第2個檔observable
);
subscribeToFirst = true;
firstOrSecond.subscribe(value => console.log(value));
// Logs:
// "first"
subscribeToFirst = false;
firstOrSecond.subscribe(value => console.log(value));
// Logs:
// "second"
常用的有
把一個 序列 轉為 另一個序列
https://rxjs-dev.firebaseapp.com/api/operators/map
map(x=>10*x)
^ ^^^^
import { fromEvent } from 'rxjs';
import { map } from 'rxjs/operators';
const clicks = fromEvent(document, 'click');
const positions = clicks.pipe(map( (ev: MouseEvent) => ev.clientX));
^^ ^^^^^^^ 轉成只要clientX
positions.subscribe(x => console.log(x));
var ob=fromEvent(document,'click')
.pipe(startWith(1,2,3)) // 多形最多6個,超過要放在陣列
^^^^^^^^^^^^^^^ 不用按click,就會先丟3個序列
.subscribe(v=>{
console.log(v);
});
輸入一個input stream、一個累加器(accumulator)、一個seed(初使化累加器的seed)
accepts 1 input stream, an accumulator, a seed (optional)
https://rxjs-dev.firebaseapp.com/api/operators/scan
看圖會比較好理解
https://reactive.how/scan
import { fromEvent } from 'rxjs';
import { scan, mapTo } from 'rxjs/operators';
const clicks = fromEvent(document, 'click');
const ones = clicks.pipe(mapTo(1));
^^^^^^^^ 每click一次,ones就是1
const seed = 0;
const count = ones.pipe( vvvvvvvvv The accumulator function
scan( (acc, one) => acc + one, seed )
^^^ return的值是acc ^^^^ 初使化accumulator,預設是undefine
);
count.subscribe(x => console.log(x));
^^^ 看到map,就是回傳observable
https://rxjs-dev.firebaseapp.com/api/operators/concatMap
https://ithelp.ithome.com.tw/articles/10188387
import { fromEvent, interval } from 'rxjs';
import { concatMap, take } from 'rxjs/operators';
const ob = fromEvent(document, 'click')
.pipe(
map( (ev: MouseEvent) => ev.clientX) ),
concat() // 如果是 merge() 就是mergeMap()
)
.subscribe(x => console.log(x));
好像會cache每個observable的序列
只有在先前的observable有complete,才會合併到exhaustMap()要回傳的Observable中
Projects each source value to an Observable which is merged in the output Observable only if the previous projected Observable has completed.
https://rxjs-dev.firebaseapp.com/api/operators/exhaustMap
常用的有:
由另一個observable來決定是否通行
Ignores source values for a duration determined by another Observable, then emits the most recent value from the source Observable, then repeats this process.
Emit clicks at a rate of at most one click per second
import { fromEvent, interval } from 'rxjs';
import { audit } from 'rxjs/operators'
const clicks = fromEvent(document, 'click');
const result = clicks.pipe( audit(ev => interval(1000)) );
^^^^^^^^^^^^^^^^^^^^ 1秒通過1個
result.subscribe(x => console.log(x));
有A,B,C 3個observables
result B拿到資料,跟C做一些事情(doSomething)
A要跟result C做一些事情(doSomething)
條件:C要在B之後
const a$ = of('1');
const bc$ = b$.pipe(mergeMap(rb=> c$(rb));
^^^^^^^
combineLatest(a$,bc$).subscribe(abc=>doSomething(abc));
^^^^^^^^^^^^^
負責將1個Observable,廣播給多個Observer(得到的都是同1個Observable丟出來的資料)
(如果不用Multicasting,有多個subscribe(),就會有多個Observable)
負責處理Observable觀察過程
中出現的例外
import { fromEvent } from 'rxjs';
import { tap, map } from 'rxjs/operators';
const clicks = fromEvent(document, 'click');
const positions = clicks.pipe(
tap(ev => console.log(ev)),
^^^ 在map()之前,跑一點程式
map(ev => ev.clientX),
);
positions.subscribe(x => console.log(x));
import { timer } from 'rxjs';
const numbers = timer(3000, 1000);
延遲 做的時間
// Emits ascending numbers, one every second (1000ms), starting after 3 seconds
// 延遲3秒,做interval 1秒
// 如果想要一開始就先做1秒,可用startWith()
// 或者time(0,多久觸發1次)
numbers.subscribe(x => console.log(x));
都要等complete 才會運算
負責判斷布林值
與條件式
相關的運算子
defaultIfEmpty
https://rxjs-dev.firebaseapp.com/api/operators/defaultIfEmpty
當Observable從開始到complete的過程都沒值產生時(都沒有資料流),會送出預設值
看圖比較好懂Emits a given value if the source Observable completes without emitting any next value, otherwise mirrors the source Observable.
find
findIndex
負責將Observable傳來的資料進行運算